﻿// --------------
// 作者:Vitity
// --------------

using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;

using NICWebApi.Extensions.RESTfulResult.Providers;

using System;

namespace NICWebApi.Extensions.RESTfulResult.Filter;

/// <summary>
/// RESTful结果过滤器
/// </summary>
public class ResultFilter : IAsyncActionFilter
{
    private readonly IUnifyResultProvider _resultProvider;

    /// <summary>
    /// 初始化异常过滤器
    /// </summary>
    /// <param name="resultProvider">结果提供器</param>
    public ResultFilter(
        IUnifyResultProvider resultProvider)
    {
        _resultProvider = resultProvider ?? throw new ArgumentNullException(nameof(resultProvider));
    }

    /// <summary>
    /// 异步执行动作过滤器
    /// </summary>
    /// <param name="context"></param>
    /// <param name="next"></param>
    /// <returns></returns>
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // 执行下一个过滤器或动作，并获取执行后的上下文
        var actionExecutedContext = await next();

        // 设置执行后的结果
        actionExecutedContext.Result = actionExecutedContext.Result;
        // 排除 WebSocket 请求处理
        //if (actionExecutedContext.HttpContext.IsWebSocketRequest()) return;

        // 如果结果是 IStatusCodeActionResult 类型，并且状态码不为空
        if (actionExecutedContext.Result is IStatusCodeActionResult statusCodeResult && statusCodeResult.StatusCode != null)
        {
            var httpContext = context.HttpContext;
            var statusCode = statusCodeResult.StatusCode.Value;

            // 解决刷新 Token 时间和 Token 时间相近问题
            // 如果状态码是 401 未授权，并且响应头包含 access-token 和 x-access-token
            if (statusCodeResult.StatusCode.Value == StatusCodes.Status401Unauthorized
                && httpContext.Response.Headers.ContainsKey("access-token")
                && httpContext.Response.Headers.ContainsKey("x-access-token"))
            {
                // 将状态码修改为 403 禁止访问
                httpContext.Response.StatusCode = statusCode = StatusCodes.Status403Forbidden;
            }

            // 如果响应已经开始输出，则禁止写入
            if (httpContext.Response.HasStarted) return;
        }

        // 如果执行过程中出现异常，则不进入该过滤器
        if (actionExecutedContext.Exception != null) return;

        var action = context.ActionDescriptor as ControllerActionDescriptor;

        // 判断是否跳过规范化处理类特性
        if (action.MethodInfo.DeclaringType.IsDefined(typeof(NotRESTfulResult), true)) return;

        // 判断是否跳过规范化处理方法特性
        if (action.MethodInfo.IsDefined(typeof(NotRESTfulResult), true)) return;

        // 处理 BadRequestObjectResult 类型的规范化处理
        if (actionExecutedContext.Result is BadRequestObjectResult badRequestObjectResult)
        {
            // 解析验证消息
            var validationMetadata = badRequestObjectResult.Value;
            // 调用 RESTfulResultProvider 的 OnValidateFailed 方法处理验证失败
            var result = _resultProvider.OnValidateFailed(context, validationMetadata);
            if (result != null) actionExecutedContext.Result = result;
        }
        else
        {
            IActionResult result = default;

            // 检查结果是否是有效的结果（可进行规范化的结果）
            if (UnifyContext.CheckVaildResult(actionExecutedContext.Result, out var data))
            {
                // 调用 RESTfulResultProvider 的 OnSucceeded 方法处理成功结果
                result = _resultProvider.OnSucceeded(actionExecutedContext, data);
            }

            // 如果结果是不能规范化的结果类型，则跳过
            if (result == null) return;

            // 设置执行后的结果
            actionExecutedContext.Result = result;
        }
    }
}